﻿using System;
using System.Collections.Generic;
using System.Text;

namespace ArgusPCLib.Threading.Collections
{
	public interface ISynchronizedStack<T> : ISynchronizedEnumerable<T>
	{
		int Count { get; }

		bool Contains(T item);
		void Push(T item);
		T Pop();
		T Peek();
		bool TryPop(out T item);
		bool TryPeek(out T item);
		void PushRange(IEnumerable<T> items);
		T[] PopRange(int count);
		T[] ToArray();
		T[] Empty();
	}

	public class SynchronizedStack<T> : SynchronizedEnumerableBase<T>, ISynchronizedStack<T>
	{
		Stack<T> stack;

		public SynchronizedStack()
			: base()
		{
			this.stack = new Stack<T>();
		}

		public SynchronizedStack(object syncRoot)
			: base(syncRoot)
		{
			this.stack = new Stack<T>();
		}

		public SynchronizedStack(IEnumerable<T> enumberable)
			: base()
		{
			this.stack = new Stack<T>(enumberable);
		}

		public SynchronizedStack(IEnumerable<T> enumerable, object syncRoot)
			: base(syncRoot)
		{
			this.stack = new Stack<T>(enumerable);
		}

		protected override ISynchronizedEnumerator<T> GetEnumeratorCore()
		{
			return new SynchronizedEnumeratorWrapper<T>(this.stack.GetEnumerator(), this.SyncRoot);
		}

		public int Count { get { lock (this.SyncRoot) { return this.stack.Count; } } }
		public bool Contains(T item) { lock (this.SyncRoot) { return this.stack.Contains(item); } }
		public void Push(T item) { lock (this.SyncRoot) { this.stack.Push(item); } }
		public T Pop() { lock (this.SyncRoot) { return this.stack.Pop(); } }
		public T Peek() { lock (this.SyncRoot) { return this.stack.Peek(); } }

		public bool TryPop(out T item)
		{
			lock (this.SyncRoot)
			{
				bool RetVal = this.stack.Count > 0;
				if (RetVal == true)
					item = this.stack.Pop();
				else
					item = default(T);
				return RetVal;
			}
		}

		public bool TryPeek(out T item)
		{
			lock (this.SyncRoot)
			{
				bool RetVal = this.stack.Count > 0;
				if (RetVal == true)
					item = this.stack.Peek();
				else
					item = default(T);
				return RetVal;
			}
		}

		public void PushRange(IEnumerable<T> items)
		{
			lock (this.SyncRoot)
			{
				foreach (T item in items)
					this.stack.Push(item);
			}
		}

		public T[] PopRange(int count)
		{
			lock(this.SyncRoot)
			{
				T[] RetVal = new T[Math.Min(count, this.stack.Count)];
				for (int i = 0; i < RetVal.Length; i++)
					RetVal[i] = this.stack.Pop();
				return RetVal;
			}
		}

		public T[] ToArray() { lock (this.SyncRoot) { return this.stack.ToArray(); } }

		public T[] Empty()
		{
			lock (this.SyncRoot)
			{
				T[] RetVal = this.stack.ToArray();
				this.stack.Clear();
				return RetVal;
			}
		}
	}

	public class SynchronizedQueue<T> : SynchronizedEnumerableBase<T>, ISynchronizedStack<T>
	{
		Queue<T> queu;

		public SynchronizedQueue()
			: base()
		{
			this.queu = new Queue<T>();
		}

		public SynchronizedQueue(object syncRoot)
			: base(syncRoot)
		{
			this.queu = new Queue<T>();
		}

		public SynchronizedQueue(IEnumerable<T> enumberable)
			: base()
		{
			this.queu = new Queue<T>(enumberable);
		}

		public SynchronizedQueue(IEnumerable<T> enumerable, object syncRoot)
			: base(syncRoot)
		{
			this.queu = new Queue<T>(enumerable);
		}

		protected override ISynchronizedEnumerator<T> GetEnumeratorCore()
		{
			return new SynchronizedEnumeratorWrapper<T>(this.queu.GetEnumerator(), this.SyncRoot);
		}

		public int Count { get { lock (this.SyncRoot) { return this.queu.Count; } } }
		public bool Contains(T item) { lock (this.SyncRoot) { return this.queu.Contains(item); } }
		public T Peek() { lock (this.SyncRoot) { return this.queu.Peek(); } }
		public void Enqueue(T item) { lock (this.SyncRoot) { this.queu.Enqueue(item); } }
		public T Dequeue() { lock (this.SyncRoot) { return this.queu.Dequeue(); } }
		void ISynchronizedStack<T>.Push(T item) { this.Enqueue(item); }
		T ISynchronizedStack<T>.Pop() { return this.Dequeue(); }

		public bool TryDequeue(out T item)
		{
			lock (this.SyncRoot)
			{
				bool RetVal = this.queu.Count > 0;
				if (RetVal == true)
					item = this.queu.Dequeue();
				else
					item = default(T);
				return RetVal;
			}
		}

		bool ISynchronizedStack<T>.TryPop(out T item) { return this.TryDequeue(out item); }

		public bool TryPeek(out T item)
		{
			lock (this.SyncRoot)
			{
				bool RetVal = this.queu.Count > 0;
				if (RetVal == true)
					item = this.queu.Peek();
				else
					item = default(T);
				return RetVal;
			}
		}

		public void EnqueueRange(IEnumerable<T> items)
		{
			lock (this.SyncRoot)
			{
				foreach (T item in items)
					this.queu.Enqueue(item);
			}
		}

		void ISynchronizedStack<T>.PushRange(IEnumerable<T> items) { this.EnqueueRange(items); }

		public T[] DequeueRange(int count)
		{
			lock(this.SyncRoot)
			{
				T[] RetVal = new T[Math.Min(count, this.queu.Count)];
				for (int i = 0; i < RetVal.Length; i++)
					RetVal[i] = this.queu.Dequeue();
				return RetVal;
			}
		}

		T[] ISynchronizedStack<T>.PopRange(int count) { return this.DequeueRange(count); }

		public T[] ToArray() { lock (this.SyncRoot) { return this.queu.ToArray(); } }

		public T[] Empty()
		{
			lock (this.SyncRoot)
			{
				T[] RetVal = this.queu.ToArray();
				this.queu.Clear();
				return RetVal;
			}
		}
	}
}
